package auth_middleware

import (
	"errors"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
	"net/http"
	"shimmer/neusoft_club_backend/dao/domain"
	"shimmer/neusoft_club_backend/services"
)

type clubQueryParam struct {
	ClubId *int64 `json:"club_id" uri:"club_id" form:"club_id"  xml:"club_id"`
}

func AuthUserMiddleware(required bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		session := sessions.Default(c)
		userID, ok := session.Get(domain.UserSessionID).(int64)
		if !ok {
			if !required {
				return
			}
			c.JSON(http.StatusForbidden, gin.H{
				"error": "forbidden please login",
			})
			c.Abort()
			return
		}
		user := domain.User{
			ID: userID,
		}
		if err := services.DBEngine.Find(&user).Error; err != nil {
			code := http.StatusInternalServerError
			if errors.Is(err, gorm.ErrRecordNotFound) {
				code = http.StatusNotFound
			}
			c.JSON(code, gin.H{
				"error": err,
			})
		}
		c.Set(domain.UserContextName, &user)
	}
}
func AuthLoginMiddleware() gin.HandlerFunc {
	return AuthUserMiddleware(true)
}
func AuthRolesMiddleware(roles domain.AuthorizationRole) gin.HandlerFunc {
	authorLoginMiddleware := AuthLoginMiddleware()
	return func(c *gin.Context) {
		if roles == 0 {
			return
		}
		authorLoginMiddleware(c)
		clubQuery := clubQueryParam{}
		err := c.ShouldBind(&clubQuery)
		if err != nil || clubQuery.ClubId == nil {
			err = c.ShouldBindUri(&clubQuery)
		}

		userInterface, exist := c.Get(domain.UserContextName)
		user, ok := userInterface.(*domain.User)
		if !exist || !ok {
			c.JSON(http.StatusInternalServerError, gin.H{
				"error": "Can't found user_service in context",
			})
			c.Abort()
			return
		}
		err = services.DBEngine.Joins(domain.UserFieldActors).Find(user).Error

		if err != nil {
			code := http.StatusInternalServerError
			if errors.Is(err, gorm.ErrRecordNotFound) {
				code = http.StatusNotFound
			}
			c.JSON(code, gin.H{
				"error": err,
			})
			c.Abort()
			return
		}
		var actorRole int64 = 0
		var targetActor *domain.UserActor = nil
		for _, userActor := range user.Actors {
			actorRole |= int64(userActor.Role)
			if clubQuery.ClubId != nil && userActor.ClubID != nil && *userActor.ClubID == *clubQuery.ClubId {
				targetActor = &userActor
			}
		}
		if clubQuery.ClubId == nil {

			if roles&domain.AuthorizationRole(actorRole) != roles {
				c.JSON(http.StatusForbidden, gin.H{
					"error": "forbidden because the roles is not satisfied",
				})
				c.Abort()
			}
			return
		}
		if targetActor == nil {
			c.JSON(http.StatusForbidden, gin.H{
				"error": "forbidden because not found target actor in user_service",
			})
			c.Abort()
			return
		}
		if domain.AuthorizationRole(actorRole)&domain.AuthAdmin == 0 || roles&domain.AuthorizationRole(actorRole) != roles {
			c.JSON(http.StatusForbidden, gin.H{
				"error": "forbidden because the roles is not satisfied",
			})
			return
		}
	}
}
